class WebSocketClient {
  constructor(url, options = {}) {
    this.url = url; // WebSocket连接URL
    this.options = options; // 原始配置选项
    this.socket = null; // WebSocket实例
    this.eventListeners = new Map(); // 事件监听器存储(key:事件名, value:回调函数数组)
    this.isTimeoutClosed = false; // 标记是否因超时关闭连接
    this.readyState = WebSocket.CONNECTING; // 当前连接状态
    this.reconnectTimer = null; // 重连计时器ID
    this.reconnectAttempts = 0; // 当前重连尝试次数
    this.isClosing = false; // 标记是否主动关闭连接
    this.messageQueue = []; // 消息队列(连接未建立时缓存消息)
    this.lastHeartbeatTime = 0; // 最后一次心跳时间戳

    // 配置默认参数
    this.config = {
      reconnectInterval: options.reconnectInterval || 3000, //重连间隔(ms)
      maxReconnectAttempts: options.maxReconnectAttempts || 10, //最大重连尝试次数
      heartbeatInterval: options.heartbeatInterval || 30000, //心跳间隔(ms)
      heartbeatMsg: options.heartbeatMsg || { type: 'ping', source: 'client' }, //心跳消息内容
      disconnectOnHidden: options.disconnectOnHidden !== false, // 是否在页面隐藏时断开连接
      hiddenDisconnectTimeout: options.hiddenDisconnectTimeout || 10000, // 页面隐藏后断开连接的超时时间(ms)
      ...options,
    };

    this.visibilityTimer = null; // 页面隐藏计时器ID

    this.init();

    this.initVisibilityListener();
  }

  //初始化页面可见性变化监听
  initVisibilityListener() {
    if (!this.config.disconnectOnHidden) return;

    this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
    document.addEventListener('visibilitychange', this.handleVisibilityChange);
  }

  //初始化WebSocket连接
  init() {
    try {
      this.socket = new WebSocket(this.url);
      this.readyState = this.socket.readyState;

      // 绑定事件处理函数
      this.socket.onopen = this.onOpen.bind(this);
      this.socket.onmessage = this.onMessage.bind(this);
      this.socket.onclose = this.onClose.bind(this);
      this.socket.onerror = this.onError.bind(this);

      // 启动心跳
      this.startHeartbeat();
    } catch (error) {
      this.emit('error', error);
      this.scheduleReconnect();
    }
  }

  // WebSocket连接成功回调
  onOpen(event) {
    this.readyState = WebSocket.OPEN;
    this.reconnectAttempts = 0;
    this.emit('open', event);

    // 发送缓存的消息
    if (this.messageQueue.length > 0) {
      this.messageQueue.forEach((msg) => this.send(msg));
      this.messageQueue = [];
    }
  }

  //WebSocket消息接收回调
  onMessage(event) {
    try {
      const data = this.parseMessage(event.data);
      // 区分服务器Pong响应
      if (data.type === 'pong' && data.source === 'server') {
        this.lastHeartbeatTime = Date.now();
        this.emit('heartbeat', { direction: 'incoming', type: 'pong' });
        return;
      }
      // 检测服务器主动发送的Ping
      if (data.type === 'ping' && data.source === 'server') {
        this.emit('heartbeat', { direction: 'incoming', type: 'ping' });
        // 自动回复Pong给服务器
        this.send({ type: 'pong', source: 'client' });
        return;
      }
      this.emit('message', data);
    } catch (error) {
      this.emit('error', new Error(`消息解析失败: ${error.message}`));
    }
  }

  // WebSocket连接关闭回调
  onClose(event) {
    this.readyState = WebSocket.CLOSED;
    this.stopHeartbeat();
    this.emit('close', event);

    // 非主动关闭且页面可见时尝试重连
    if (!this.isClosing && !this.isTimeoutClosed && !document.hidden) {
      this.scheduleReconnect();
    }
  }

  //WebSocket错误处理回调
  onError(error) {
    this.emit('error', error);
    // 不直接重连，依赖onClose触发重连
  }

  //处理页面可见性变化事件 页面隐藏时启动断开计时器，页面显示时重置计时器并尝试重连
  handleVisibilityChange() {
    if (document.visibilityState === 'hidden') {
      // 页面隐藏，开始计时
      this.startHiddenTimer();
      console.log('页面隐藏了>>>→➡️➡️➡️');
    } else {
      // 页面显示，重置计时器
      this.resetHiddenTimer();
      console.log('页面显示了>>>→➡️➡️➡️');
      // 无论当前状态如何，强制重置状态并尝试重连
      this.isTimeoutClosed = false;
      this.isClosing = false;
      this.reconnectAttempts = 0;
      this.clearReconnectTimer();
      if (
        this.readyState === WebSocket.CLOSED ||
        this.readyState === WebSocket.CLOSING
      ) {
        this.init();
      }
    }
  }

  //启动页面隐藏计时器  当页面隐藏超时时关闭WebSocket连接
  startHiddenTimer() {
    this.clearHiddenTimer();
    this.visibilityTimer = setTimeout(() => {
      if (document.visibilityState === 'hidden' && this.isConnected()) {
        this.emit('disconnecting_due_to_hidden');
        this.close(1000, 'Page hidden timeout', true);
      }
    }, this.config.hiddenDisconnectTimeout);
  }

  //重置页面隐藏计时器  清除现有计时器并更新最后可见时间
  resetHiddenTimer() {
    this.clearHiddenTimer();
    this.lastVisibleTime = Date.now();
  }

  // 清理页面隐藏计时器   清除超时计时器并重置计时器ID
  clearHiddenTimer() {
    if (this.visibilityTimer) {
      clearTimeout(this.visibilityTimer);
      this.visibilityTimer = null;
    }
  }

  /**
   * 公共重连方法，允许外部手动触发WebSocket连接
   * 重置重连状态并尝试初始化连接
   */
  reconnect() {
    if (this.readyState !== WebSocket.OPEN) {
      this.isTimeoutClosed = false;
      this.isClosing = false;
      this.reconnectAttempts = 0;
      this.clearReconnectTimer();
      this.init();
      console.log('外部触发WebSocket重连');
    }
  }

  //检查WebSocket连接是否处于打开状态
  isConnected() {
    return this.readyState === WebSocket.OPEN;
  }

  // 解析WebSocket接收到的消息
  parseMessage(data) {
    try {
      // 尝试解析JSON
      return JSON.parse(data);
    } catch {
      // 普通文本消息
      return data;
    }
  }

  //发送消息到WebSocket服务器
  send(data, options = {}) {
    if (this.readyState !== WebSocket.OPEN) {
      if (options.queue !== false) {
        // 默认启用消息队列
        this.messageQueue.push(data);
        this.emit('queued', data);
      } else {
        throw new Error('WebSocket连接未建立');
      }
      return;
    }

    const message = typeof data === 'string' ? data : JSON.stringify(data);
    this.socket.send(message);
    this.emit('sent', data);
  }

  // 发送请求并等待响应
  sendRequest(data, timeout = 5000) {
    const requestId = Math.random().toString(36).substring(2, 10);
    return new Promise((resolve, reject) => {
      // 发送带ID的请求
      this.send({ ...data, requestId });

      // 监听特定响应
      const responseHandler = (response) => {
        if (response.requestId === requestId) {
          this.off('message', responseHandler);
          resolve(response);
        }
      };

      this.on('message', responseHandler);

      // 设置超时
      setTimeout(() => {
        this.off('message', responseHandler);
        reject(new Error('请求超时'));
      }, timeout);
    });
  }

  // 关闭连接
  close(code = 1000, reason = 'Normal closure', isTimeout = null) {
    if (isTimeout !== null) {
      this.isTimeoutClosed = isTimeout;
    }
    this.isClosing = true;
    this.clearReconnectTimer();
    if (this.socket) {
      this.socket.close(code, reason);
    }
    this.stopHeartbeat();
  }

  // 注册事件监听器
  on(event, callback) {
    if (!this.eventListeners.has(event)) {
      this.eventListeners.set(event, []);
    }
    this.eventListeners.get(event).push(callback);
    return this;
  }

  // 销毁实例
  destroy() {
    this.close();
    document.removeEventListener(
      'visibilitychange',
      this.handleVisibilityChange
    );
    this.clearHiddenTimer();
    this.eventListeners.clear();
  }

  // 移除事件监听器
  off(event, callback) {
    if (this.eventListeners.has(event)) {
      const listeners = this.eventListeners.get(event);
      const index = listeners.indexOf(callback);
      if (index !== -1) {
        listeners.splice(index, 1);
      }
    }
    return this;
  }

  // 触发事件
  emit(event, data) {
    const listeners = this.eventListeners.get(event);
    if (listeners) {
      listeners.forEach((callback) => callback(data));
    }
  }

  // 安排重连
  scheduleReconnect() {
    if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
      this.emit('reconnect_failed', this.reconnectAttempts);
      return;
    }

    this.reconnectAttempts++;
    // 使用指数退避算法计算重连延迟
    const delay =
      this.config.reconnectInterval * Math.min(1, this.reconnectAttempts * 0.5);

    this.emit('reconnect_scheduled', {
      attempt: this.reconnectAttempts,
      delay,
    });
    this.reconnectTimer = setTimeout(() => {
      this.emit('reconnect_attempt', this.reconnectAttempts);
      this.init();
    }, delay);
  }

  // 清理重连计时器
  clearReconnectTimer() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
  }

  // 启动心跳
  startHeartbeat() {
    this.stopHeartbeat();
    this.lastHeartbeatTime = Date.now();

    // 添加网络状态监听
    this.networkStateListener = () => {
      if (!navigator.onLine && this.readyState !== WebSocket.CLOSED) {
        this.emit('network_offline');
        this.socket.close(4001, 'Network disconnected');
      }
    };
    window.addEventListener('online', this.networkStateListener);
    window.addEventListener('offline', this.networkStateListener);

    this.heartbeatTimer = setInterval(() => {
      // 双重检查连接状态和网络状态
      if (this.readyState === WebSocket.OPEN && navigator.onLine) {
        this.send(this.config.heartbeatMsg, { isHeartbeat: true });
        this.emit('heartbeat_sent');

        // 设置超时检测
        this.heartbeatTimeoutTimer = setTimeout(() => {
          const elapsed = Date.now() - this.lastHeartbeatTime;
          if (elapsed > this.config.heartbeatInterval * 2) {
            this.emit('heartbeat_timeout');
            this.socket.close(4008, 'Heartbeat timeout');
          }
        }, this.config.heartbeatInterval * 2);
      } else if (!navigator.onLine) {
        // 网络离线时主动关闭连接触发重连
        this.socket.close(4001, 'Network disconnected');
      }
    }, this.config.heartbeatInterval);
  }

  // 停止心跳
  stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
    if (this.heartbeatTimeoutTimer) {
      clearTimeout(this.heartbeatTimeoutTimer);
      this.heartbeatTimeoutTimer = null;
    }
    // 清除可见性延迟定时器
    if (this.visibilityTimer) {
      clearTimeout(this.visibilityTimer);
      this.visibilityTimer = null;
    }
    // 移除网络状态监听
    if (this.networkStateListener) {
      window.removeEventListener('online', this.networkStateListener);
      window.removeEventListener('offline', this.networkStateListener);
      this.networkStateListener = null;
    }
    // 移除页面可见性监听
    if (this.handleVisibilityChange) {
      document.removeEventListener(
        'visibilitychange',
        this.handleVisibilityChange
      );
    }
  }
}

export default WebSocketClient;
